// Copyright Dotware Technologies 2005. Author Hadi Asghari.
// DBaseRecordSet.cpp : Implementation of CDBaseRecordSet

#include "stdafx.h"
#include "DBaseRecordSet.h"
#include ".\dbaserecordset.h"



/* Notes:
	1. We are using CDbfRecordSet, but CDbfCursorSet might also be useful.
	2. These methods seem interesting for export:
		a) m_Set.GetDateLastUpdated()
		b) m_Set.LockRecord()
	3. Different dBase versions differ in memo-type fields, and locking.
	4. Helpstrings can be added for all exported methods if required.
	5. We are receiving 'warning MIDL2039', meaning = ?
*/

extern void IranSystemTextConv(CString &s, TextConvertDirection a);


STDMETHODIMP CDBaseRecordSet::Open(BSTR filename)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	TRY
	{		
		char tfilename[256];
		::wcstombs(tfilename, filename, 255);
		m_Set.Open(tfilename);
		m_Set.SetTextConvertFunction(IranSystemTextConv);
		// TODO: add a property for codepage 

		return S_OK;
	}
	CATCH_ALL(e)
	{
		return S_FALSE;
	}
	END_CATCH_ALL
}

STDMETHODIMP CDBaseRecordSet::IsEof(BYTE* retVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	*retVal = m_Set.IsEOF();

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::GetFieldCount(SHORT* retVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	*retVal = m_Set.GetFieldCount();

	return S_OK;
}


STDMETHODIMP CDBaseRecordSet::MoveNext(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	m_Set.MoveNext();

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::Close(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	m_Set.Close();

	return S_OK;
}

// TODO: GetFieldValueS and GetFieldValueI can be both overloaded as GetFieldValue
STDMETHODIMP CDBaseRecordSet::GetFieldValueS(BSTR fieldname, VARIANT* retVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	char tfieldname[256];
	::wcstombs(tfieldname, fieldname, 255);
	
	COleVariant cov;
	long l = m_Set.GetFieldValue(tfieldname, cov);
	*retVal = cov.Detach(); 

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::GetFieldValueI(SHORT index, VARIANT* retVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	COleVariant cov;
	cov = m_Set.GetFieldValue(index);
	*retVal = cov.Detach(); 

	return S_OK;
}

// TODO: for 'type', an enum can be used
STDMETHODIMP CDBaseRecordSet::GetFieldInfo(SHORT index, BSTR* name, CHAR* type, SHORT* length, SHORT* decimal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	*type = m_Set.m_DBFFields[index].m_chType;
	*length = m_Set.m_DBFFields[index].m_nLen;
	*decimal = m_Set.m_DBFFields[index].m_nDec;
	
	CComBSTR fname = m_Set.m_DBFFields[index].m_strName;
	*name = fname.Detach();

	return S_OK;
}


STDMETHODIMP CDBaseRecordSet::GetRecordCount(ULONG* retVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	*retVal = m_Set.GetRecordCount();

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::CreateTable(BSTR fileName, BYTE* retVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	
	char tfileName[256];
	::wcstombs(	tfileName, fileName, 255);
	*retVal = m_Set.CreateTable(tfileName);
	
	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::AddFieldC(BSTR fieldName, SHORT fieldLength)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	char tfieldName[256];
	::wcstombs(tfieldName, fieldName, 255);
	FIELD_REC FieldRec;

	memset(&FieldRec, 0, sizeof(FIELD_REC));
	memcpy(FieldRec.field_name, tfieldName, 10);
	FieldRec.field_type = FLD_TYPE_CHARACTER;
	FieldRec.len_info.char_len = fieldLength;
	m_Set.AddField(&FieldRec);

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::AddFieldN(BSTR fieldName, BYTE fieldLength, BYTE fieldDecimal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	char tfieldName[256];
	::wcstombs(tfieldName, fieldName, 255);
	FIELD_REC FieldRec;

	memset(&FieldRec, 0, sizeof(FIELD_REC));
	memcpy(FieldRec.field_name, tfieldName, 10);
	FieldRec.field_type = FLD_TYPE_NUMERIC;
	FieldRec.len_info.num_size.len = fieldLength;
	FieldRec.len_info.num_size.dec = fieldDecimal;
	m_Set.AddField(&FieldRec);

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::UpdateTable(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	m_Set.UpdateTable();

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::AddFieldL(BSTR fieldName)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	char tfieldName[256];
	::wcstombs(tfieldName, fieldName, 255);
	FIELD_REC FieldRec;
	// dosn't set logial field properly
	memset(&FieldRec, 0, sizeof(FIELD_REC));
	memcpy(FieldRec.field_name, tfieldName, 10);
	FieldRec.field_type = FLD_TYPE_LOGICAL;
	FieldRec.len_info.char_len = 1;
	m_Set.AddField(&FieldRec);
	
	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::SetFieldValue(BSTR fieldName, VARIANT fieldValue)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	char tfieldName[256];
	::wcstombs(tfieldName, fieldName, 255);

	m_Set.SetFieldValue(tfieldName, fieldValue);

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::Edit(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	m_Set.Edit();

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::Update(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	m_Set.Update();

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::AddNew(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	m_Set.AddNew();

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::Delete(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	m_Set.Delete();

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::MoveLast(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	m_Set.MoveLast();

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::MoveFirst(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	m_Set.MoveFirst();

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::MovePrev(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	m_Set.MovePrev();

	return S_OK;
}
STDMETHODIMP CDBaseRecordSet::IsOpen(BYTE* retVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	*retVal = m_Set.IsOpen();

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::FindFirst(BSTR filter, BYTE* retVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	TRY
	{
		char tfilter[256];
		// TODO: the filter-striing must be changed from unicode
		::wcstombs(	tfilter, filter, 255);
		*retVal = m_Set.FindFirst( tfilter );

		return S_OK;
	}
	CATCH_ALL(e)
	{
		return S_FALSE; // TODO: return more detailed error code if needed
	}
	END_CATCH_ALL

}

STDMETHODIMP CDBaseRecordSet::FindNext(BSTR filter, BYTE* retVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	TRY
	{
		char tfilter[256];
		::wcstombs(	tfilter, filter, 255);
		*retVal = m_Set.FindNext( tfilter );

		return S_OK;
	}
	CATCH_ALL(e)
	{
		return S_FALSE;
	}
	END_CATCH_ALL
}

STDMETHODIMP CDBaseRecordSet::FindPrev(BSTR filter, BYTE* retVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	TRY
	{
		char tfilter[256];
		::wcstombs(	tfilter, filter, 255);
		*retVal = m_Set.FindPrev( tfilter );

		return S_OK;
	}
	CATCH_ALL(e)
	{
		return S_FALSE;
	}
	END_CATCH_ALL
}

STDMETHODIMP CDBaseRecordSet::FindLast(BSTR filter, BYTE* retVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	
	TRY
	{
		char tfilter[256];
		::wcstombs(	tfilter, filter, 255);
		*retVal = m_Set.FindLast( tfilter );

		return S_OK;
	}
	CATCH_ALL(e)
	{
		return S_FALSE;
	}
	END_CATCH_ALL
}

/* TODO: correct syntaxes:
1- use SAFEARRAY instead of pointer to variants
2- use our own ENUMS instead of -1, ...
3- use properties instead of IsEOF, ..
4- use our own struct types (eg for field-types) instead of passing four variables.
5- use method overriding instead of addfieldc, addfieldn, ...
*/

// this method will return an IntPtr in C#, and must be converted to object [] :
//		System.Runtime.InteropServices.Marshal.GetObjectsForNativeVariants(ptr, count);
// BTW, COM methods can also pass struct types

STDMETHODIMP CDBaseRecordSet::GetRecord(VARIANT * retVal [])
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	int count = m_Set.GetFieldCount();
	VARIANT * arrayVar  = new VARIANT[count];

	for ( int index = 0; index < count; index++ )
	{
		arrayVar[index] = m_Set.GetFieldValue(index).Detach();
	}
	
	*retVal = arrayVar;  
	// TODO: who will free the above array?! -

	return S_OK;
}

// this method is used to free the record array allocated in the above method,
// unfortunatly in C# it must be called by  IntPtr.ToInt32()  
STDMETHODIMP CDBaseRecordSet::FreeRecord(LONG ptr)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	VARIANT *v = reinterpret_cast<VARIANT *> (ptr);
	delete v;

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::get_DbfId(BYTE* pVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	*pVal = m_Set.m_pTableDef->m_DBFHead.dbf_id;

/*	DBF_UCHAR dbf_id;		// 0x02 FoxBASE
							// 0x03 FoxBASE+/dBASE III PLUS, no memo
							// 0x30 Visual FoxPro
							// 0x43 dBASE IV SQL table files, no memo
							// 0x63 dBASE IV SQL system files, no memo
							// 0x83 FoxBASE+/dBASE III PLUS, with memo
							// 0x8B dBASE IV with memo
							// 0xCB dBASE IV SQL table files, with memo
							// 0xE5 HiPer-SIx with memo (.SMT)
							// 0xF5 FoxPro 2.x (or earler) with memo
							// 0xFB FoxBASE*/

	return S_OK;

	//m_Set.m_pTableDef->m_DBFFields;
	//m_Set.m_pTableDef->m_DBFHead.
	//m_Set.m_DBFFields[0].get_Status();
}

STDMETHODIMP CDBaseRecordSet::get_DbfRecordSize(ULONG* pVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	*pVal = m_Set.m_pTableDef->m_DBFHead.rec_size;

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::get_DbfCodePage(BYTE* pVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	*pVal = m_Set.m_pTableDef->m_DBFHead.code_page;

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::get_LastUpdate(DATE* pVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	// TODO: 
	//*pVal = m_Set.GetDateLastUpdated()

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::get_Position(LONG* pVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	*pVal = m_Set.GetAbsolutePosition(); 

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::put_Position(LONG newVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	m_Set.SetAbsolutePosition(newVal);

	return S_OK;
}

STDMETHODIMP CDBaseRecordSet::get_TableFlag(BYTE* pVal)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	*pVal = m_Set.m_pTableDef->m_DBFHead.table_flag;
							// 0x01 file has structural .cdx or .mdx
							// 0x02 file has a Memo field
							// 0x04 file is a database .dbc
	return S_OK;
}
